/*
 * @Author: caiyuzheng
 * @Date: 2021-10-06 23:05:26
 * @LastEditTime: 2021-11-18 11:14:20
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \generallib\general\src\threadsafe\thread_safe_list.h
 */
#pragma once

#include <list>
#include <mutex>
#include <condition_variable>

using namespace std;
template <class T>
class ThreadSafeList
{
public:
    ThreadSafeList()
    {
    }

    ~ThreadSafeList()
    {
    }
    ThreadSafeList(ThreadSafeList<T> &b){
        this->m_data =  b.m_data;
    }

    
    int PushBack(T v)
    {
        m_mux.lock();
        m_data.push_back(v);
        m_mux.unlock();
        return 0;
    }
    int PushFront(T v)
    {
        m_mux.lock();
        m_data.push_front(v);
        m_mux.unlock();
        m_cv.notify_one();
        return 0;
    }
    int Emplace(int pos, T v)
    {
        m_mux.lock();
        auto itr = m_data.begin();
        for (int i = 0; itr != m_data.end(); itr++)
        {
            if (i == pos)
            {
                m_data.emplace(itr, v);
                break;
            }
            i++;
        }
        m_mux.unlock();
        return 0;
    }
    int EmplaceEnd(T v)
    {
        m_mux.lock();
        auto itr = m_data.begin();
        for (int i = 0; itr != m_data.end(), itr++)
        {
            i++;
            if (i == pos)
                break;
        }
        m_data.emplace(itr, v);
        m_mux.unlock();
        return 0;
    }
    using vIterator =  typename std::list<T>::iterator;
    vIterator Front()
    {
        if (!m_data.empty())
        {
            m_mux.lock();
            auto ret = m_data.begin();
            m_mux.unlock();
            return ret;
        }
        return m_data.end();
    }

    T PopFrontAndWait()
    {
        if (!m_data.empty())
        {
            m_mux.lock();
            auto ret = m_data.begin();
            auto ret1 = *ret;
            m_data.erase(ret);
            m_mux.unlock();
            return ret1;
        }else{
            std::unique_lock<std::mutex> guard(m_mux);
            m_cv.wait(guard);
            auto ret = m_data.begin();
            auto ret1 = *ret;
            m_data.erase(ret);
            return ret1;
        }
    }

    T PopEnd()
    {
        if (!m_data.empty())
        {
            m_mux.lock();
            auto ret = m_data.end();
            m_data.erase(ret);
            m_mux.unlock();
            return ret;
        }
    }
    uint32_t Length()
    {
        return m_data.size();
    }
    
private:
    std::mutex m_mux;
    std::condition_variable m_cv;
    std::list<T> m_data;
};
